package gecp.math;

import gecp.edge.Location;

/**
 * Angle
 *
 * @author Cosmin
 */
public class Angle {

    /**
     * Compute the angle in degree between 2 points
     *
     * @param x0
     * @param y0
     * @param x1
     * @param y1
     *
     * @return The angle in degree
     */
    public static double computeAngle(double x0, double y0, double x1, double y1) {
        double deltaX = x1 - x0;
        double deltaY = -1 * (y1 - y0);
        double inRads = Math.atan2(deltaY, deltaX);
        if (inRads < 0) {
            inRads = Math.abs(inRads);
        } else {
            inRads = 2 * Math.PI - inRads;
        }
        return Angle.affinityAngle(Math.toDegrees(inRads));
    }

    /**
     * Compute the location from an x and an y for an amplitude and a angle in
     * degree
     *
     * @param x The x axis
     * @param y The y axis
     * @param amplitude The amplitude
     * @param angle The angle in degree
     *
     * @return
     */
    public static Location computeLocation(double x, double y, double amplitude, double angle) {
        double moveX = x + amplitude * Math.cos(Math.toRadians(angle));
        double moveY = y + amplitude * Math.sin(Math.toRadians(angle));
        if (moveX < 1 || moveY < 1) {
            moveX *= 10;
            moveY *= 10;
        }
        return new Location((int) moveX, (int) moveY);
    }

    /**
     * Affinity angle between 0 and 360
     *
     * @param angle The angle
     *
     * @return The affinity angle
     */
    public static double affinityAngle(double angle) {
        if (angle < 0) {
            while (angle < 0) {
                angle += 360;
            }
        } else if (angle >= 360) {
            while (angle >= 360) {
                angle -= 360;
            }
        }
        return angle;
    }

    /**
     * Affinity angle between 0 and 360 for integers
     *
     * @param angle The angle
     *
     * @return The affinity angle
     */
    public static int affinityAngle(int angle) {
        if (angle < 0) {
            while (angle < 0) {
                angle += 360;
            }
        } else if (angle >= 360) {
            while (angle >= 360) {
                angle -= 360;
            }
        }
        return angle;
    }

    /**
     * Adjust the angle to point to a pixel near by the current pixel. The
     * return values are 0, 45, 90, 135, 180, 225, 270 and 315
     *
     * @param angle The current angle in degree
     *
     * @return The adjusted angle
     */
    public static int adjustAngle(double angle) {
        int fixAngle = (int) angle;
        int da = 0;
        boolean done = false;
        while (!done) {
            if (Math.abs(angle - da) < 22.5) {
                fixAngle = da;
                done = true;
            }
            da += 45;
            if (da > 360) {
                break;
            }
        }
        return Angle.affinityAngle(fixAngle);
    }

    /**
     * Return the absolute difference between 2 angles in degree. The result is
     * always between 0 and 180 degree.
     *
     * @param a1 The first angle
     * @param a2 The second angle
     *
     * @return The absolute difference
     */
    public static double absAngleDifference(double a1, double a2) {
        double ang1 = a1;
        double ang2 = a2;
        if (ang2 > ang1) {
            ang1 += 360;
        }
        double diff = ang1 - ang2;
        if (diff > 180) {
            diff -= 180;
        }
        return diff;
    }
}
